﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace SiteParser.Core
{
	public class XPathHtmlIterator : IHtmlIterator
	{
		private readonly string xPath;
		private readonly IExpressionParser expressionParser;
		private readonly string htmlContent;

		private bool isCompiled = false;
		private bool isFinished = false;
		private IList<HtmlNavigationExpression> expressionList;
		private Stack<HtmlNavigationExpression> executedExpressionsStack;
		private Stack<HtmlNavigationExpression> notExecutedExpressionStack;
		private Stack<IHtmlIterator> executedIteratorsStack;

		internal XPathHtmlIterator(string htmlContent, string xPath, IExpressionParser expressionParser) {
			if (string.IsNullOrEmpty(xPath)) {
				throw new ArgumentNullException("xPath");
			}
			if (expressionParser == null) {
				throw new ArgumentNullException("expressionParser");
			}
			if (htmlContent == null)
			{
				throw new ArgumentNullException("htmlContent");
			}

			this.htmlContent = htmlContent;
			this.expressionParser = expressionParser;
			this.xPath = xPath;
		}

		public void Compile() {
			expressionList = expressionParser.Parse(xPath);
			executedExpressionsStack = new Stack<HtmlNavigationExpression>(expressionList);
			executedIteratorsStack = new Stack<IHtmlIterator>();
			notExecutedExpressionStack = new Stack<HtmlNavigationExpression>();

			while (executedExpressionsStack.Count > 0) {
				notExecutedExpressionStack.Push(executedExpressionsStack.Pop());
			}

			var rootExpression = notExecutedExpressionStack.Pop();
			var rootIterator = HtmlIteratorFactory.CreateIterator(htmlContent, rootExpression);

			executedIteratorsStack.Push(rootIterator);
			executedExpressionsStack.Push(rootExpression);

			isCompiled = true;
		}

		#region IHtmlIterator Members

		public bool MoveNext() {
			if (!isCompiled) {
				Compile();
			}

			executedIteratorsStack.Peek().MoveNext();

			if (notExecutedExpressionStack.Count > 0 || !executedIteratorsStack.Peek().Success) {
				var htmlIterator = executedIteratorsStack.Peek();

				while (notExecutedExpressionStack.Count > 0 || !htmlIterator.Success) {
					//back to the first success iterator
					while (htmlIterator != null && !htmlIterator.Success && executedIteratorsStack.Count > 0) {
						notExecutedExpressionStack.Push(executedExpressionsStack.Pop());
						executedIteratorsStack.Pop();
						htmlIterator = executedIteratorsStack.Count > 0 ? executedIteratorsStack.Peek() : null;
						if (htmlIterator != null) {
							htmlIterator.MoveNext();
						}
					}

					if (htmlIterator == null) {
						break;
					}

					bool isNodeRiched = false;
					//down to the last success iterator
					do {
						var expression = notExecutedExpressionStack.Peek();

						htmlIterator = HtmlIteratorFactory.CreateIterator(htmlIterator.SelectedValue, expression);
						isNodeRiched = htmlIterator.MoveNext();

						if (isNodeRiched) {
							executedExpressionsStack.Push(notExecutedExpressionStack.Pop());
							executedIteratorsStack.Push(htmlIterator);
						}
						else {
							htmlIterator = executedIteratorsStack.Peek();
							htmlIterator.MoveNext();
						}
					}
					while (isNodeRiched && notExecutedExpressionStack.Count > 0);
				}
			}

			return Success;
		}

		public bool Success {
			get { return notExecutedExpressionStack.Count == 0 && executedIteratorsStack.Peek().Success; }
		}

		public string SelectedValue {
			get { return Success ? executedIteratorsStack.Peek().SelectedValue : null; }
		}

		#endregion
	}
}
